package org.example.myRateLimiter;

import lombok.Data;
import org.example.MyMethodInvocation;

import java.util.concurrent.locks.ReentrantLock;

//令牌桶限流器
//用可重入锁来控制令牌的增减   一个方法有一个这玩意 所以不能是单例 或者根据方法区分
@Data
public class TokenBucketRateLimiter implements MyRateLimiter {
    private final String name = "tokenBucketRateLimiter";// 限流器的名称给他写死了
    private int capacity = 10;  // 桶的容量
    private int rate = 5;      // 每秒生成的令牌数
    private int tokens = 5; // 当前令牌数 初始获得10个
    private long lastRefillTime;  // 上次生成令牌的时间

    // 用来控制令牌的更新逻辑 适用于生成频率高于获取频率的场景 不然还不如 开一个线程每秒生成
    private final ReentrantLock lock = new ReentrantLock();

    //根据容量和速率初始化限流器默认是100,10
    public TokenBucketRateLimiter(int capacity, int rate) {
        if (capacity > 0) {
            setCapacity(capacity);
        }
        if (rate > 0) {
            setRate(rate);
            setTokens(rate);
        }
        System.out.println("当前令牌 : " + tokens);
        this.lastRefillTime = System.currentTimeMillis();
    }


    // 判断是否允许请求
    public boolean isAllowed() {
        refillTokens();  // 每次请求前，检查并更新令牌数

        boolean success = false;

        //如果减成功 则成功
        if (tokens > 0) {
            lock.lock();
            if (tokens > 0) {
                tokens--;
                success = true;
            }
            lock.unlock();
        }

        return success;

    }

    @Override
    public Object tryExecute(MyMethodInvocation realMethodInvocation, MyMethodInvocation fallbackMethodInvocation) {
        if (isAllowed()) {
            try {
                return realMethodInvocation.invoke();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        //不然返回那个方法
        try {
            return fallbackMethodInvocation.invoke();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 令牌桶的自动补充方法
    private void refillTokens() {
        long currentTime = System.currentTimeMillis();

        // 每秒补充令牌
        if (currentTime - lastRefillTime > 1000) {
            lock.lock();
            //再检查一下
            if (currentTime - lastRefillTime > 1000) {
                int newTokens = (int) ((currentTime - lastRefillTime) / 1000) * rate;
                tokens = Math.min(tokens + newTokens, capacity);
                System.out.println("当前令牌数：" + tokens);
                lastRefillTime = currentTime; // 更新上次补充令牌的时间
            }
            lock.unlock();
        }
    }
}
